package com.zelin.search.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.zelin.entity.ItemEntity;
import com.zelin.entity.vo.ItemVo;
import com.zelin.search.service.ItemSearchService;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.document.StringField;
import org.apache.lucene.queryparser.xml.builders.BooleanQueryBuilder;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.ObjectInputStream;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * ------------------------------
 * 功能：
 * 作者：WF
 * 微信：hbxfwf13590332912
 * 创建时间：2021/8/2-16:06
 * ------------------------------
 */
@Service
public class ItemSearchServiceImpl implements ItemSearchService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ElasticsearchRestTemplate restTemplate;
    /**
     * 功能: 根据查询参数得到查询结果
     * 参数:
     * 返回值: java.util.Map
     * 时间: 2021/8/2 16:06
     * @param params
     */
    @Override
    public Map<String, Object> search(ItemVo params) {

        System.out.println("ItemVo = " + params);
        //1. 得到查询关键字
        String keyword = params.getKeywords();
        if(StringUtils.isBlank(keyword)){
            keyword = "华为";
        }

        //2. 定义返回结果
        Map<String, Object> resultMap  = new HashMap<>();

        //3. 定义NativeSearchQueryBuilder对象
        //3.1 得到当前页
        int page = params.getPage();
        //3.2 得到每页大小
        int pageSize = params.getPageSize();
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder()
                                                      .withPageable(PageRequest.of(page - 1,pageSize));

        //4. 按关键字进行分组查询 [ 显示数据的查询 ]
        searchQueryBuilder.addAggregation(AggregationBuilders.terms("categoryGroup")
                                                             .field("category.keyword").size(50));

        //5. 添加高亮查询
        //5.1 设置高亮查询
        searchQueryBuilder.withHighlightBuilder(new HighlightBuilder()
                .field("title") //设置高亮字段
                .preTags("<span style='color:red'>")  //设置高亮显示内容的前缀部分
                .postTags("</span>"));                //设置高亮显示内容的后缀部分
        //6. 添加多字段查询
        searchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery( keyword,"title","brand","category"));

        //7. 定义过滤查询对象(性能优于must查询)，用于组合多个查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //7.1 分类查询
        if(StringUtils.isNotBlank(params.getCategory())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category.keyword",params.getCategory()));
        }
        //7.2 品牌查询
        if(StringUtils.isNotBlank(params.getBrand())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand.keyword",params.getBrand()));
        }
        //7.3 添加规格查询
        if(params.getSpec() != null && params.getSpec().size() > 0){
            for (String spec : params.getSpec()) {
                //7.3.1 处理得到的规格数据
                String[] split = spec.split(":"); //网络：移动3G
                //7.3.2 进行规格的过滤查询
                boolQueryBuilder.filter(QueryBuilders.termQuery("specMap."+split[0]+".keyword",split[1]));
            }
        }
        //7.4 价格区间查询
        String price = params.getPrice();
        if(StringUtils.isNotBlank(price)){
            //7.4.1 拆出出两个价格区间
            String[] s = price.split("_");
            //7.4.2 判断是否结束值带有*号
            if(!s[1].equals("*")){    //0-500
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gt(s[0]).lte(s[1]));
            }else{                    //3000-*
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(s[0]));
            }
        }

        //7.5 按照升序、降序排序查询
        //7.5.1 得到排序关键字
        String sort = params.getSort();
        if(StringUtils.isNotBlank(sort)){
            //7.5.2 拆分字符串
            String[] split = sort.split(":");       //数组第一部分：升序/降序 第二部分：排序字段
            //7.5.3 进行排序
            searchQueryBuilder.withSort(SortBuilders.fieldSort(split[1]).order(split[0].equals("asc") ? SortOrder.ASC : SortOrder.DESC));
        }

        //7.6 添加过滤查询
        searchQueryBuilder.withFilter(boolQueryBuilder);

        //8. 得到查询对象
        NativeSearchQuery searchQuery = searchQueryBuilder.build();

        //9. 得到命中对象
        SearchHits<ItemEntity> searchHits = restTemplate.search(searchQuery, ItemEntity.class, IndexCoordinates.of("item"));
        //10. 得到命中的结果
        List<SearchHit<ItemEntity>> list = searchHits.getSearchHits();
        //11. 得到命中结果的选项
        long total = searchHits.getTotalHits();     //总记录数
        int totalPage = (int) Math.ceil(total/(double)pageSize);//总页数
        //12. 处理命中结果(原始的关键字查询)
        //List<ItemEntity> collect = list.stream().map(m -> m.getContent()).collect(Collectors.toList());

        //13. 得到分组数据
        Aggregations aggregations = searchHits.getAggregations();
        //14. 得到关于分类的分组
        ParsedStringTerms categoryGroupResult = aggregations.get("categoryGroup");
        //15. 处理分类分组的结果数据
        List<String> categoryList = categoryGroupResult.getBuckets().stream()
                .map(m -> m.getKeyAsString()).collect(Collectors.toList());
        System.out.println("categoryList = " + categoryList);

        //16. 得到高亮查询的结果
        //16.1 定义存放高亮字段的内容
        List<ItemEntity> highlights = new ArrayList<>();
        //16.2 遍历所有命中的数据，从中挑选出高亮数据
        for (SearchHit<ItemEntity> searchHit : list) {   //list: 所有命中的结果对象
            //16.3 可以直接通过高亮字段名称得到此高亮字段的值
            List<String> title = searchHit.getHighlightField("title");

            //16.4 得到未高亮前的数据
            ItemEntity itemEntity = searchHit.getContent();
            //16.5 定义存放高亮字段的字符串
            StringBuffer buffer = new StringBuffer();
            //16.6 组合高亮字段的值
            for (String s : title) {
                buffer.append(s);
            }
            //16.7 将高亮字段的值重新设置回原来的对象
            itemEntity.setTitle(buffer.toString());
            //16.8 将高亮对象添加到集合中
            highlights.add(itemEntity);
        }
        System.out.println("highlights = " + highlights);

        //17. 从redis中得到品牌及规格列表
        Map brandAndSpecMap = new HashMap();
        //18. 得到用户选择的分类
        String category = params.getCategory();
        if(StringUtils.isBlank(category)){
            if(categoryList != null && categoryList.size() > 0) {
                category = categoryList.get(0);
            }
        }

        //19. 根据分类进行查询品牌及规格
        brandAndSpecMap = findBrandAndSpecList(category);
        //20. 将品牌及规格列表放到大集合中
        resultMap.putAll(brandAndSpecMap);
        //21. 存放高亮数据到大集合
        resultMap.put("rows",highlights);               //当前分页记录集合
        resultMap.put("categoryList",categoryList);     //分类
        resultMap.put("total",total);                   //总记录数
        resultMap.put("totalPage",totalPage);           //总页数

        return resultMap;
    }

    /**
     * 功能: 将所有的商品添加到索引库中（增量更新）
     * 参数:
     * 返回值: void
     * 时间: 2021/8/5 16:54
     */
    @Override
    public void updateToIndex(List<ItemEntity> entities) {
        restTemplate.save(entities);
    }
    /**
     * 功能: 根据分类名称得到品牌列表及规格列表
     * 参数:
     * 返回值: java.util.Map
     * 时间: 2021/8/3 8:39
     */
    private Map findBrandAndSpecList(String category) {
        //1. 根据分类名称得到模板id
        Object typeId =  redisTemplate.boundHashOps("itemCats").get(category);

        //2. 根据模板id得到品牌列表
        List<Map> brandList = (List<Map>) redisTemplate.boundHashOps("brandList").get(typeId);
        //3. 根据模板id得到规格列表
        List<Map> specList = (List<Map>) redisTemplate.boundHashOps("specList").get(typeId);
        //4. 定义结果map
        Map brandAndSpecmap = new HashMap();
        //5. 将上面得到的品牌列表及规格列表都放到map中
        brandAndSpecmap.put("brandList",brandList);
        brandAndSpecmap.put("specList",specList);
        //6. 返回
        return brandAndSpecmap;
    }

    /**
     * 功能: 从索引库中根据goodsid删除商品
     * 参数:
     * 返回值:
     * 时间: 2021/8/9 15:45
     */
    public void deleteSku(List<Long> goodsIds){
        //1. 构造查询条件
        Query query = new CriteriaQuery(new Criteria("goodsId").in(goodsIds));
        //2. 从索引库中删除记录
        restTemplate.delete(query,ItemEntity.class,IndexCoordinates.of("item"));
    }
}
